/**
 * 随机密码库：密码验证
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
//应该使用.h头文件
#include "RandomSequence.c"
#include "AES10.c" //注意。是AES10
#include <RandomNative.h>
//==========================
// unsigned long long seed; // 种子

const int HIGH_CODE_LENGTH = 87616; //高位编码长度
const int LOW_CODE_LENGTH = 97536;  //低位编码长度
// const int LOW_COE_REPETITION_NUM = 10; //低位编码副本数量
const int HIGH_NUM_LENGTH = 5;      //高位编码位数
const char HIGH_NUM_PREFIX[] = "0"; //高位编码前缀

const char HName[] = "HArray";
const char LName[] = "LArray";
//==========================

//==========================
void initRK(long seed1, long seed2);
void verifyPwd();
int getLocalTimeHour();
int doParse(int p1, int p2, time_t nowHour, char *pwd);
//==========================

int main(int argc, char const *argv[])
{
    /*== 必要参数 ================================== */
    time_t initHour = 1546272000L / 60 / 60; // 2019-1-1 00:00:00 基准时间,使用北京时间
    long seed1 = 100000019;                  // 种子1
    long seed2 = 100000019;                  // 种子2
    char Apwd[] = "501994994";               // 用户输入一个密码。

    /*== AES10 解密 ================================== */
    unsigned char key[] = "1234567890123456"; //iv 和 key 可以相同，并且支持数字字母字符,长度必须 16，即根密钥
    unsigned char ivs[] = "1234567890123456"; // 密钥以联调时分配的为准
    set_key(key);
    set_mode(MODE_CFB); //CFB   支持
    set_iv(ivs);
    char pwd[] = {}; //密码解密后
    Decrypt(Apwd, sizeof Apwd, pwd);

    /*== 初始化密码库 ================================== */
    initRK(seed1, seed2); //初始化密码库， 不应该在这儿做,是在设备初始化的时候做。

    /*== 执行校验 ================================== */
    time_t nowHour = getLocalTimeHour(); //获取当前北京时间【精确到小时】
    printf("now:%d\n", nowHour);
    char pwd1[HIGH_NUM_LENGTH] = {};
    strncpy(pwd1, pwd, HIGH_NUM_LENGTH); //1.分割密码
    pwd1[HIGH_NUM_LENGTH] = '\0';
    int start = atoi(pwd1); //开始时间
    char pwd2[sizeof(pwd) - HIGH_NUM_LENGTH] = {};
    strncpy(pwd2, pwd + HIGH_NUM_LENGTH, sizeof(pwd) - HIGH_NUM_LENGTH);
    int end = atoi(pwd2); //结束时间
    printf("混淆值：%d-%d\n", start, end);
    //  校验
    int p1 = getFlashData(HName, start) + initHour; //真实时间 TODO
    int p2 = getFlashData(LName, end) / 10;         //偏移值 TODO

    if (doParse(p1, p2, nowHour, pwd))
        printf("密码验证成功");
    else
        printf("密码验证失败");
}

void initRK(long seed1, long seed2)
{
    //=====================
    setSeed(seed1);                       //设置种子
    getSequence(HName, HIGH_CODE_LENGTH); //生成高位数组
    //======================
    setSeed(seed2);                      //设置种子
    getSequence(LName, LOW_CODE_LENGTH); //生成低位数组
}

/**
 * 这里是获取本地时间，精确到小时
 */
int getLocalTimeHour()
{
    time_t now;
    time(&now);       //获取当前时间，精确到秒
    now = now / 3600; //小时；
    printf("%ld\n", now);
    return now;
}

/**
 * 解析 随机密码 
 * @param p1 高位：开始时间hour
 * @param p2 低位：偏移量hour
 * @param nowHour  当前时间hour
 * @param pwd  输入的密码
 * @return 校验结果
 */
int doParse(int p1, int p2, time_t nowHour, char *pwd)
{
    int uselog = existPwd(pwd); // 检查历史成功密码是否存在。TODO
    if (nowHour < p1)
        return 0;                               //"密码开始时间不在有效期内";
    if (uselog == 0 && abs(nowHour - p1) > 24) //历史记录没有，且密码24小时内没用过
        return 0;                               //密码未在24小时内使用，已经作废
    //=======================================================
    if (p2 <= 8760) //1常规密码,
    {
        // if (p1 + p2 < nowHour) // [FIX:20190929:1] 将 <  改为 <=
        if (p1 + p2 <= nowHour)
            return 0; // "密码结束时间不在有效期内";
    }
    else if (p2 > 8760 && p2 <= 8988) //1常规密码,
    {
        // [FIX:20190906:2] 调整月份结束时间的计算方式。
        time_t day1 = p1 * 3600L;                  // 开始时间
        time_t dayn = getLocalTimeHour() * 3600L;  // 当前时间
        int offsetM = compareToMounth(day1, dayn); // 获取真实月份差值
        // 密码月差值 >=真实月差值 && ...
        if (!(p2 - 8760 >= offsetM) && (getDayOfMounth(day1) >= getDayOfMounth(dayn)) && (getHourOfDay(day1) >= getHourOfDay(dayn))) // 能否开门
            return 0;                                                                                                                // "密码结束时间不在有效期内";
    }
    else if (p2 > 8988 && p2 <= 9288) //2单次密码,
    {
        if (uselog != 0) //
            return 0;    // 判断密码是否使用过。使用过则作废
    }
    else if (p2 > 9288 && p2 <= 9498) //3永久密码,
    {
        // return 1;
    }
    else if (p2 > 9498 && p2 <= 9508) //4清空密码,
    {
        clearPwd(); // 清空所有历史密码 TODO
        return 1;   //直接返回成功，不存储
    }
    else if (p2 >= 9509) //5循环密码
    {
        int loopStrategy = (p2 - 9509) / 24 + 1; //循环策略(1周一循环,2周二循环,3周三循环,4周四循环,5周五循环,6周六循环,7周天循环,8工作日循环,9周末循环,10每日循环)
        int loopEndPoint = (p2 - 9509) % 24;     //结束小时
        time_t now;
        struct tm *p;
        time(&now); //这里是返回北京时间
        p = gmtime(&now);
        int week = p->tm_wday;       //星期 – 取值区间为[0,6]，其中0代表星期天，1代表星期一
        int hour = p->tm_hour;       //时 - 取值区间为[0,23]
        week = week == 0 ? 7 : week; //把星期天变成7

        // [FIX:20190906:1] 调整开始时间和结束时间点算法
        loopEndPoint = loopEndPoint == 0 ? 24 : loopEndPoint; //纠正结束点
        int beginPoint = (p1 + 8) % 24;                       //设置开始点
        if (!((loopStrategy == week || (loopStrategy == 8 && week <= 5) || (loopStrategy == 9 && week >= 6) || loopStrategy == 10) && (loopEndPoint > hour && beginPoint <= hour)))
            return 0;
    }
    putPwd(pwd); //存储成功的密码 TODO
    return 1;
}
